import tkinter as tk
from tkinter import ttk, messagebox, filedialog
import json
import os
from PIL import Image, ImageTk
import numpy as np
import cv2
import time
import copy

class AnnotationTool(tk.Toplevel):
    def __init__(self, master, dataset_path, original_image_path):
        super().__init__(master)
        self.title("标注纠正工具")
        self.geometry("1400x800")
        
        self.dataset_path = dataset_path
        self.original_image_path = original_image_path
        
        # 设置正确标注文件路径
        self.correct_annotations_file = os.path.join(dataset_path, "correct_annotations.json")
        
        # 标签映射
        self.label_mapping = {
            '0': "音符0",
            '1': "音符1",
            '2': "音符2",
            '3': "音符3",
            '4': "音符4",
            '5': "音符5",
            '6': "音符6",
            '7': "音符7",
            'n': "音符",  # 保留通用音符标签
            'b': "大括号",
            'd': "点",
            'f': "降号",
            'h': "横线",
            'sh': "升号",
            'sl': "连音线",
            've': "竖线",
            'vi': "颤音线",
            'unknown': "未知"
        }
        
        # 标签顺序列表（用于循环切换）
        self.label_order = list(self.label_mapping.keys())
        
        self.filtered_indices = []  # 存储有效标注的索引
        self.confirmed_count = 0    # 已确认的标注数量
        self.annotation_history = {}  # 存储每个标注的历史状态
        self.current_states = {}     # 存储每个标注的当前状态
        self.load_and_filter_dataset()  # 加载并过滤数据集
        
        # 加载原图
        self.original_image = cv2.imread(original_image_path)
        if self.original_image is None:
            self.original_image = np.zeros((600, 800, 3), dtype=np.uint8)
        
        # 界面布局
        self.create_widgets()
        self.current_index = 0
        if self.filtered_indices:
            self.display_item(0)
        else:
            messagebox.showinfo("提示", "没有有效的标注需要纠正")
            self.destroy()
        
        # 绑定快捷键
        self.bind("<Key>", self.handle_key_press)
        self.focus_set()  # 确保窗口获得焦点
        
        # 绑定窗口大小调整事件
        self.bind("<Configure>", self.on_window_resize)
        
        # 设置关闭事件
        self.protocol("WM_DELETE_WINDOW", self.on_closing)
    
    def refresh_current_image(self):
        """刷新当前显示的图像"""
        if hasattr(self, 'current_index') and self.filtered_indices:
            # 获取当前原始索引
            original_index = self.filtered_indices[self.current_index]
            item = self.dataset["annotations"][original_index]
            
            # 重新显示原图
            self.display_original_image_with_box(item["position"])
    def on_window_resize(self, event):
        """当窗口大小改变时重新显示当前图像"""
        if event.widget == self and hasattr(self, 'current_index'):
            # 延迟执行，确保窗口调整完成
            self.after(100, self.refresh_current_image)

    def load_and_filter_dataset(self):
        """加载并过滤数据集"""
        # 加载数据集
        dataset_file = os.path.join(self.dataset_path, "dataset.json")
        with open(dataset_file, 'r') as f:
            self.dataset = json.load(f)
        
        # 过滤掉置信度低于阈值的标注
        self.filtered_indices = []
        for i, item in enumerate(self.dataset["annotations"]):
            # 初始化当前状态
            self.current_states[i] = {
                "corrected_label": item.get("corrected_label", item["predicted_label"]),
                "timestamp": time.strftime("%Y-%m-%d %H:%M:%S")
            }
            
            # 初始化历史记录
            self.annotation_history[i] = [copy.deepcopy(self.current_states[i])]
            
            # 添加到过滤列表
            self.filtered_indices.append(i)
        
        self.total_items = len(self.filtered_indices)
        
        # 加载已确认的标注计数
        self.confirmed_count = 0
        for item in self.dataset["annotations"]:
            if "corrected_label" in item and item["corrected_label"] == item["predicted_label"]:
                self.confirmed_count += 1
    
    def is_valid_annotation(self, item):
        """检查标注是否有效"""
        # 过滤标签为'none'的标注
        if item.get("predicted_label", "") == "none":
            return False
        
        # 过滤没有图像切片的标注
        slice_path = item.get("slice_path", "")
        if not slice_path or not os.path.exists(slice_path):
            return False
        
        # 过滤无效位置
        position = item.get("position", [])
        if len(position) != 4:
            return False
        
        # 过滤无效置信度
        confidence = item.get("confidence", -1)
        if confidence < 0:
            return False
        
        return True
    
    # def create_widgets(self):
    #     # 主框架
    #     main_frame = tk.Frame(self)
    #     main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
        
    #     # 状态栏（显示总标注数和当前位置）
    #     # 状态栏（显示总标注数和当前位置）
    #     status_frame = tk.Frame(self)
    #     status_frame.pack(fill=tk.X, padx=10, pady=5)
        
    #     self.status_label = tk.Label(status_frame, text="", font=("Arial", 10))
    #     self.status_label.pack(side=tk.LEFT)
        
    #     # 快捷键提示
    #     shortcut_label = tk.Label(status_frame, text="快捷键: A-上一张 | D-下一张 | S/W-切换标签 | E-标记为未知 | U-撤销", 
    #                             font=("Arial", 10), fg="blue")
    #     shortcut_label.pack(side=tk.RIGHT, padx=10)
        
    #     # 左侧：原图显示（带滚动条）
    #     left_frame = tk.Frame(main_frame)
    #     left_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        
    #     # 创建画布容器
    #     canvas_container = tk.Frame(left_frame)
    #     canvas_container.pack(fill=tk.BOTH, expand=True)
        
    #     # 创建水平和垂直滚动条
    #     h_scrollbar = tk.Scrollbar(canvas_container, orient=tk.HORIZONTAL)
    #     v_scrollbar = tk.Scrollbar(canvas_container, orient=tk.VERTICAL)
        
    #     # 创建画布
    #     self.original_canvas = tk.Canvas(
    #         canvas_container,
    #         xscrollcommand=h_scrollbar.set,
    #         yscrollcommand=v_scrollbar.set,
    #         bg="white"  # 设置背景色以便看到画布区域
    #     )
        
    #     # 配置滚动条
    #     h_scrollbar.config(command=self.original_canvas.xview)
    #     v_scrollbar.config(command=self.original_canvas.yview)
        
    #     # 使用网格布局
    #     self.original_canvas.grid(row=0, column=0, sticky="nsew")
    #     v_scrollbar.grid(row=0, column=1, sticky="ns")
    #     h_scrollbar.grid(row=1, column=0, sticky="ew")
        
    #     # 配置网格行列权重
    #     canvas_container.grid_rowconfigure(0, weight=1)
    #     canvas_container.grid_columnconfigure(0, weight=1)
        
    #     # 在画布上创建用于显示图像的框架
    #     self.image_frame = tk.Frame(self.original_canvas)
    #     self.image_window = self.original_canvas.create_window(
    #         (0, 0), 
    #         window=self.image_frame, 
    #         anchor=tk.NW
    #     )
        
    #     # 绑定事件以调整框架大小
    #     self.image_frame.bind("<Configure>", self.on_frame_configure)
    #     self.original_canvas.bind("<Configure>", self.on_canvas_configure)
        
    #     # 绑定鼠标滚轮事件
    #     self.original_canvas.bind("<MouseWheel>", self.on_mousewheel)  # Windows
    #     self.original_canvas.bind("<Button-4>", self.on_mousewheel)    # Linux
    #     self.original_canvas.bind("<Button-5>", self.on_mousewheel)    # Linux
        
    #     # 右侧：轮廓切片显示和控制面板
    #     right_frame = tk.Frame(main_frame, width=400)
    #     right_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True)
        
    #     # 轮廓切片显示
    #     self.slice_frame = tk.Frame(right_frame)
    #     self.slice_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
        
    #     self.slice_canvas = tk.Canvas(self.slice_frame)
    #     self.slice_canvas.pack(fill=tk.BOTH, expand=True)
        
    #     # 控制面板
    #     control_frame = tk.Frame(right_frame)
    #     control_frame.pack(fill=tk.X, padx=10, pady=5)
        
    #     # 标签选择（带中文含义）
    #     tk.Label(control_frame, text="选择正确标签:").pack(side=tk.LEFT)
    #     self.label_var = tk.StringVar()
        
    #     # 创建带中文含义的标签列表
    #     self.labels_with_meaning = []
    #     for label, meaning in self.label_mapping.items():
    #         self.labels_with_meaning.append(f"{label} ({meaning})")
        
    #     self.label_combo = ttk.Combobox(control_frame, textvariable=self.label_var, 
    #                                    values=self.labels_with_meaning, width=15)
    #     self.label_combo.pack(side=tk.LEFT, padx=5)
        
    #     # 导航按钮
    #     nav_frame = tk.Frame(control_frame)
    #     nav_frame.pack(side=tk.RIGHT)
        
    #     tk.Button(nav_frame, text="上一张 (A)", command=self.prev_item).pack(side=tk.LEFT, padx=2)
    #     tk.Button(nav_frame, text="下一张 (D)", command=self.next_item).pack(side=tk.LEFT, padx=2)
    #     tk.Button(nav_frame, text="标记未知 (E)", command=self.mark_as_unknown).pack(side=tk.LEFT, padx=10)
    #     tk.Button(nav_frame, text="撤销更改 (U)", command=self.undo_changes).pack(side=tk.LEFT, padx=10)
        
    #     # 信息显示
    #     self.info_label = tk.Label(control_frame, text="")
    #     self.info_label.pack(side=tk.LEFT, padx=10)
        
    #     # 添加保存按钮
    #     save_frame = tk.Frame(right_frame)
    #     save_frame.pack(fill=tk.X, padx=10, pady=5)
        
    #     tk.Button(save_frame, text="选择保存文件", command=self.select_save_file, 
    #              bg="#2196F3", fg="white").pack(side=tk.LEFT, padx=5)
        
    #     tk.Button(save_frame, text="保存并退出", command=self.save_and_exit, 
    #              bg="#4CAF50", fg="white").pack(side=tk.LEFT, padx=5)
        
    #     tk.Button(save_frame, text="退出不保存", command=self.destroy, 
    #              bg="#f44336", fg="white").pack(side=tk.RIGHT, padx=5)
        
    #     # 已确认标注显示
    #     confirmed_frame = tk.Frame(right_frame)
    #     confirmed_frame.pack(fill=tk.X, padx=10, pady=5)
        
    #     self.confirmed_label = tk.Label(confirmed_frame, text="已确认标注: 0", font=("Arial", 10, "bold"))
    #     self.confirmed_label.pack(side=tk.LEFT)
        
    #     # 状态历史显示
    #     history_frame = tk.Frame(right_frame)
    #     history_frame.pack(fill=tk.X, padx=10, pady=5)
        
    #     self.history_label = tk.Label(history_frame, text="历史状态: 无", font=("Arial", 9))
    #     self.history_label.pack(side=tk.LEFT)
    
    def create_widgets(self):
        # 主框架
        main_frame = tk.Frame(self)
        main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
        
        # 使用grid布局，左侧占70%，右侧占30%
        main_frame.grid_columnconfigure(0, weight=7)  # 左侧列权重为7
        main_frame.grid_columnconfigure(1, weight=3)  # 右侧列权重为3
        main_frame.grid_rowconfigure(0, weight=1)
        
        # 左侧：原图显示（带滚动条）
        left_frame = tk.Frame(main_frame)
        left_frame.grid(row=0, column=0, sticky="nsew", padx=(0, 10))  # 添加右边距
        
        # 创建画布容器
        canvas_container = tk.Frame(left_frame)
        canvas_container.pack(fill=tk.BOTH, expand=True)
        
        # 创建水平和垂直滚动条
        h_scrollbar = tk.Scrollbar(canvas_container, orient=tk.HORIZONTAL)
        v_scrollbar = tk.Scrollbar(canvas_container, orient=tk.VERTICAL)
        
        # 创建画布
        self.original_canvas = tk.Canvas(
            canvas_container,
            xscrollcommand=h_scrollbar.set,
            yscrollcommand=v_scrollbar.set,
            bg="white",
            width=800,  # 增加初始宽度
            height=600   # 增加初始高度
        )
        
        # 配置滚动条
        h_scrollbar.config(command=self.original_canvas.xview)
        v_scrollbar.config(command=self.original_canvas.yview)
        
        # 使用网格布局
        self.original_canvas.grid(row=0, column=0, sticky="nsew")
        v_scrollbar.grid(row=0, column=1, sticky="ns")
        h_scrollbar.grid(row=1, column=0, sticky="ew")
        
        # 配置网格行列权重
        canvas_container.grid_rowconfigure(0, weight=1)
        canvas_container.grid_columnconfigure(0, weight=1)
        
        # 在画布上创建用于显示图像的框架
        self.image_frame = tk.Frame(self.original_canvas)
        self.image_window = self.original_canvas.create_window(
            (0, 0), 
            window=self.image_frame, 
            anchor=tk.NW
        )
        
        # 绑定事件以调整框架大小
        self.image_frame.bind("<Configure>", self.on_frame_configure)
        self.original_canvas.bind("<Configure>", self.on_canvas_configure)
        
        # 绑定鼠标滚轮事件
        self.original_canvas.bind("<MouseWheel>", self.on_mousewheel)  # Windows
        self.original_canvas.bind("<Button-4>", self.on_mousewheel)    # Linux
        self.original_canvas.bind("<Button-5>", self.on_mousewheel)    # Linux
        
        # 右侧：轮廓切片显示和控制面板
        right_frame = tk.Frame(main_frame)
        right_frame.grid(row=0, column=1, sticky="nsew")
        
        # 轮廓切片显示
        self.slice_frame = tk.Frame(right_frame)
        self.slice_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=(0, 10))  # 减少上下边距
        
        self.slice_canvas = tk.Canvas(self.slice_frame)
        self.slice_canvas.pack(fill=tk.BOTH, expand=True)
        
        # 控制面板 - 使用紧凑布局
        control_frame = tk.Frame(right_frame)
        control_frame.pack(fill=tk.X, padx=10, pady=5)
        
        # 使用网格布局紧凑排列控件
        tk.Label(control_frame, text="选择正确标签:").grid(row=0, column=0, sticky="w")
        self.label_var = tk.StringVar()
        
        # 创建带中文含义的标签列表
        self.labels_with_meaning = []
        for label, meaning in self.label_mapping.items():
            self.labels_with_meaning.append(f"{label} ({meaning})")
        
        # 设置下拉框为只读
        self.label_combo = ttk.Combobox(control_frame, textvariable=self.label_var, 
                                       values=self.labels_with_meaning, width=15, state="readonly")
        self.label_combo.grid(row=0, column=1, sticky="ew", padx=5)
        
        # 阻止组合框捕获键盘事件
        self.label_combo.bind('<Key>', lambda e: 'break')
        
        # 导航按钮 - 紧凑排列
        nav_frame = tk.Frame(control_frame)
        nav_frame.grid(row=1, column=0, columnspan=2, pady=5, sticky="ew")
        
        tk.Button(nav_frame, text="上一张 (A)", command=self.prev_item).pack(side=tk.LEFT, fill=tk.X, expand=True, padx=2)
        tk.Button(nav_frame, text="下一张 (D)", command=self.next_item).pack(side=tk.LEFT, fill=tk.X, expand=True, padx=2)
        tk.Button(nav_frame, text="标记未知 (E)", command=self.mark_as_unknown).pack(side=tk.LEFT, fill=tk.X, expand=True, padx=2)
        tk.Button(nav_frame, text="撤销 (U)", command=self.undo_changes).pack(side=tk.LEFT, fill=tk.X, expand=True, padx=2)
        
        # 信息显示 - 使用紧凑布局
        self.info_label = tk.Label(control_frame, text="", justify=tk.LEFT)
        self.info_label.grid(row=2, column=0, columnspan=2, sticky="w")
        
        # 添加保存按钮 - 紧凑排列
        save_frame = tk.Frame(right_frame)
        save_frame.pack(fill=tk.X, padx=10, pady=(0, 5))
        
        tk.Button(save_frame, text="选择保存文件", command=self.select_save_file, 
                 bg="#2196F3", fg="white").pack(side=tk.LEFT, fill=tk.X, expand=True, padx=2)
        
        tk.Button(save_frame, text="保存并退出", command=self.save_and_exit, 
                 bg="#4CAF50", fg="white").pack(side=tk.LEFT, fill=tk.X, expand=True, padx=2)
        
        tk.Button(save_frame, text="退出不保存", command=self.destroy, 
                 bg="#f44336", fg="white").pack(side=tk.LEFT, fill=tk.X, expand=True, padx=2)
        
        # 状态栏（显示总标注数和当前位置）
        status_frame = tk.Frame(self)
        status_frame.pack(fill=tk.X, padx=10, pady=5)
        
        self.status_label = tk.Label(status_frame, text="", font=("Arial", 10))
        self.status_label.pack(side=tk.LEFT)
        
        # 快捷键提示
        shortcut_label = tk.Label(status_frame, text="快捷键: A-上一张 | D-下一张 | S/W-切换标签 | E-标记为未知 | U-撤销", 
                                font=("Arial", 10), fg="blue")
        shortcut_label.pack(side=tk.RIGHT, padx=10)
        
        # 已确认标注显示 - 紧凑布局
        confirmed_frame = tk.Frame(right_frame)
        confirmed_frame.pack(fill=tk.X, padx=10, pady=2)
        
        self.confirmed_label = tk.Label(confirmed_frame, text="已确认标注: 0", font=("Arial", 10, "bold"))
        self.confirmed_label.pack(side=tk.LEFT)
        
        # 状态历史显示 - 紧凑布局
        history_frame = tk.Frame(right_frame)
        history_frame.pack(fill=tk.X, padx=10, pady=2)
        
        self.history_label = tk.Label(history_frame, text="历史状态: 无", font=("Arial", 9))
        self.history_label.pack(side=tk.LEFT)

    def on_frame_configure(self, event):
        """当框架大小改变时更新滚动区域"""
        # 更新滚动区域以包含整个框架
        self.original_canvas.configure(scrollregion=self.original_canvas.bbox("all"))
    
    def on_canvas_configure(self, event):
        """当画布大小改变时调整内部框架大小"""
        # 获取画布的新尺寸
        canvas_width = event.width
        canvas_height = event.height
        
        # 获取图像尺寸（如果有图像）
        if hasattr(self, 'tk_original_img'):
            img_width = self.tk_original_img.width()
            img_height = self.tk_original_img.height()
            
            # 如果图像比画布小，则居中显示
            if img_width < canvas_width and img_height < canvas_height:
                # 计算居中位置
                x = (canvas_width - img_width) // 2
                y = (canvas_height - img_height) // 2
                
                # 移动图像窗口到中心
                self.original_canvas.coords(self.image_window, x, y)
                
                # 设置框架尺寸为图像尺寸
                self.image_frame.config(width=img_width, height=img_height)
            else:
                # 如果图像比画布大，允许滚动
                self.original_canvas.coords(self.image_window, 0, 0)
                self.image_frame.config(width=max(canvas_width, img_width), 
                                      height=max(canvas_height, img_height))
        else:
            # 没有图像时，设置框架尺寸为画布尺寸
            self.image_frame.config(width=canvas_width, height=canvas_height)
    
    def on_mousewheel(self, event):
        """处理鼠标滚轮事件以实现滚动"""
        # Windows 和 Mac
        if event.num == 4 or event.delta > 0:
            self.original_canvas.yview_scroll(-1, "units")
        elif event.num == 5 or event.delta < 0:
            self.original_canvas.yview_scroll(1, "units")
    
    def update_status(self):
        """更新状态栏信息"""
        status_text = f"总有效标注: {self.total_items} | 当前位置: {self.current_index + 1}/{self.total_items}"
        
        # 确保状态标签存在
        if hasattr(self, 'status_label'):
            self.status_label.config(text=status_text)
        
        # 更新已确认标注显示
        if hasattr(self, 'confirmed_label'):
            self.confirmed_label.config(text=f"已确认标注: {self.confirmed_count}")
        
        # 更新已确认标注显示
        self.confirmed_label.config(text=f"已确认标注: {self.confirmed_count}")
    
    def select_save_file(self):
        """选择保存正确标注的文件"""
        file_path = filedialog.asksaveasfilename(
            defaultextension=".json",
            filetypes=[("JSON files", "*.json"), ("All files", "*.*")],
            title="选择保存正确标注的文件"
        )
        
        if file_path:
            self.correct_annotations_file = file_path
            messagebox.showinfo("保存路径", f"正确标注将保存到: {file_path}")
    
    def handle_key_press(self, event):
        """处理键盘快捷键"""
        key = event.char.lower()
        if key == 'a':
            self.prev_item()      # A键：上一张
        elif key == 'd':
            self.next_item()      # D键：下一张
        elif key == 's':
            self.cycle_label(1)   # S键：下一个标签
        elif key == 'w':
            self.cycle_label(-1)  # W键：上一个标签
        elif key == 'e':
            self.mark_as_unknown() # E键：标记为未知
        elif key == 'u':
            self.undo_changes()   # U键：撤销更改
        
        # 确保焦点保持在主窗口
        self.focus_set()
    
    def cycle_label(self, direction):
        """循环切换标签类型"""
        current_label = self.label_var.get()
        
        # 如果当前没有选择标签，使用预测标签
        if not current_label:
            original_index = self.filtered_indices[self.current_index]
            item = self.dataset["annotations"][original_index]
            predicted_label = item['predicted_label']
            current_label = f"{predicted_label} ({self.label_mapping.get(predicted_label, '未知')})"
        
        # 获取当前标签在列表中的位置
        try:
            current_index = self.labels_with_meaning.index(current_label)
        except ValueError:
            current_index = 0
        
        # 计算新标签索引
        new_index = (current_index + direction) % len(self.labels_with_meaning)
        
        # 设置新标签
        self.label_var.set(self.labels_with_meaning[new_index])
        
        # 更新当前状态
        self.update_current_state()
    
    def mark_as_unknown(self):
        """将当前标注标记为未知并保存"""
        # 设置标签为unknown
        self.label_var.set("unknown (未知)")
        
        # 保存标注
        self.save_correction()
    
    def undo_changes(self):
        """撤销当前标注的更改，恢复到上次保存状态"""
        current_idx = self.filtered_indices[self.current_index]
        
        if len(self.annotation_history[current_idx]) > 1:
            # 恢复到上一次保存的状态
            self.annotation_history[current_idx].pop()  # 移除当前状态
            self.current_states[current_idx] = copy.deepcopy(self.annotation_history[current_idx][-1])
            
            # 更新UI
            self.display_item(self.current_index)
            messagebox.showinfo("撤销成功", "已恢复到上次保存状态")
        else:
            messagebox.showinfo("撤销", "没有可撤销的更改")
    
    def update_current_state(self, event=None):
        """更新当前标注的状态"""
        current_idx = self.filtered_indices[self.current_index]
        
        # 获取用户选择的标签（去除中文含义部分）
        selected_label = self.label_var.get().split(" ")[0]
        
        # 更新当前状态
        self.current_states[current_idx] = {
            "corrected_label": selected_label,
            "timestamp": time.strftime("%Y-%m-%d %H:%M:%S")
        }
        
        # 更新历史状态显示
        self.update_history_display()
        
        # 更新历史状态显示
        self.update_history_display()
    
    def update_history_display(self):
        """更新历史状态显示"""
        current_idx = self.filtered_indices[self.current_index]
        history_count = len(self.annotation_history[current_idx])
        
        if history_count > 1:
            self.history_label.config(text=f"历史状态: {history_count} 次更改 (按U撤销)")
        else:
            self.history_label.config(text="历史状态: 无更改")
    
    # def display_original_image_with_box(self, position):
    #     """在原图上显示当前轮廓的位置框"""
    #     # 清除框架上的旧图像
    #     for widget in self.image_frame.winfo_children():
    #         widget.destroy()
        
    #     # 创建原图的副本
    #     display_image = self.original_image.copy()
        
    #     if position and len(position) == 4:
    #         y_min, y_max, x_min, x_max = position
            
    #         # 在原图上绘制矩形框
    #         cv2.rectangle(display_image, (x_min, y_min), (x_max, y_max), (0, 0, 255), 2)
            
    #         # 显示当前轮廓编号
    #         text = f"ID: {self.current_index + 1}/{self.total_items}"
    #         cv2.putText(display_image, text, (x_min, y_min - 10), 
    #                     cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2)
        
    #     # 转换图像格式用于显示
    #     display_image = cv2.cvtColor(display_image, cv2.COLOR_BGR2RGB)
    #     height, width = display_image.shape[:2]
        
    #     # 转换为PIL图像格式
    #     pil_image = Image.fromarray(display_image)
    #     self.tk_original_img = ImageTk.PhotoImage(pil_image)
        
    #     # 在框架上显示图像
    #     img_label = tk.Label(self.image_frame, image=self.tk_original_img)
    #     img_label.image = self.tk_original_img  # 保持引用
    #     img_label.pack()
        
    #     # 更新框架尺寸
    #     self.image_frame.config(width=width, height=height)
        
    #     # 更新滚动区域
    #     self.original_canvas.configure(scrollregion=(0, 0, width, height))
        
    #     # 重置视图到左上角
    #     self.original_canvas.xview_moveto(0)
    #     self.original_canvas.yview_moveto(0)
    def display_original_image_with_box(self, position):
        """在原图上显示当前轮廓的位置框"""
        # 清除框架上的旧图像
        for widget in self.image_frame.winfo_children():
            widget.destroy()
        
        # 检查原始图像路径是否有效
        if not os.path.isfile(self.original_image_path):
            print(f"错误: 原始图像文件不存在: {self.original_image_path}")
            self.image_frame.config(text=f"无法加载原始图像: {self.original_image_path}")
            return
        
        try:
            # 加载原始图像
            self.original_image = cv2.imread(self.original_image_path)
            if self.original_image is None:
                raise ValueError("cv2.imread返回None")
        except Exception as e:
            print(f"错误: 加载原始图像失败: {str(e)}")
            self.image_frame.config(text=f"加载原始图像失败: {str(e)}")
            return
        
        # 创建原图的副本
        display_image = self.original_image.copy()
        
        if position and len(position) == 4:
            try:
                y_min, y_max, x_min, x_max = position
                # 在原图上绘制矩形框
                cv2.rectangle(display_image, (x_min, y_min), (x_max, y_max), (0, 0, 255), 2)
                
                # 显示当前轮廓编号
                text = f"ID: {self.current_index + 1}/{self.total_items}"
                cv2.putText(display_image, text, (x_min, y_min - 10), 
                            cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2)
            except Exception as e:
                print(f"警告: 绘制矩形框失败: {str(e)}")
        
        # 转换图像格式用于显示
        display_image = cv2.cvtColor(display_image, cv2.COLOR_BGR2RGB)
        height, width = display_image.shape[:2]
        
        # 转换为PIL图像格式
        pil_image = Image.fromarray(display_image)
        self.tk_original_img = ImageTk.PhotoImage(pil_image)
        
        # 在框架上显示图像
        img_label = tk.Label(self.image_frame, image=self.tk_original_img)
        img_label.image = self.tk_original_img  # 保持引用
        img_label.pack()
        
        # 更新框架尺寸
        self.image_frame.config(width=width, height=height)
        
        # 更新滚动区域
        self.original_canvas.configure(scrollregion=(0, 0, width, height))
        
        # 重置视图到左上角
        self.original_canvas.xview_moveto(0)
        self.original_canvas.yview_moveto(0)


    def display_item(self, index):
        # 获取过滤后的索引
        if index < 0 or index >= len(self.filtered_indices):
            return
        
        # 获取原始索引
        original_index = self.filtered_indices[index]
        item = self.dataset["annotations"][original_index]
        
        # 显示原图并框出当前轮廓位置
        self.display_original_image_with_box(item["position"])
        
        # 显示轮廓切片
        self.slice_canvas.delete("all")
        slice_path = item.get("slice_path")
        
        if slice_path and os.path.isfile(slice_path):
            try:
                # 使用PIL加载图像，比OpenCV更可靠
                img = Image.open(slice_path)
                img = img.resize((400, 400), Image.LANCZOS)
                self.tk_slice_img = ImageTk.PhotoImage(img)
                self.slice_canvas.create_image(200, 200, image=self.tk_slice_img)
            except Exception as e:
                print(f"错误: 加载切片图像失败: {str(e)}")
                self.slice_canvas.create_text(200, 200, text=f"无法加载切片图像\n{str(e)}", font=("Arial", 12))
        else:
            error_msg = "无可用图像切片"
            if slice_path:
                error_msg += f"\n路径: {slice_path}"
            self.slice_canvas.create_text(200, 200, text=error_msg, font=("Arial", 12))
        
        # 获取当前状态
        current_state = self.current_states[original_index]
        corrected_label = current_state["corrected_label"]
        
        # 显示信息（带中文含义）
        predicted_label = item['predicted_label']
        predicted_meaning = self.label_mapping.get(predicted_label, "未知")
        
        info = f"项目 {index+1}/{self.total_items}\n"
        info += f"预测标签: {predicted_label} ({predicted_meaning})\n"
        info += f"置信度: {item['confidence']:.2f}\n"
        
        if corrected_label:
            corrected_meaning = self.label_mapping.get(corrected_label, "未知")
            info += f"当前标注: {corrected_label} ({corrected_meaning})"
        else:
            info += f"当前标注: 未标注"
        
        self.info_label.config(text=info)
        
        # 设置当前标签（带中文含义）
        if corrected_label:
            self.label_var.set(f"{corrected_label} ({self.label_mapping.get(corrected_label, '未知')})")
        else:
            self.label_var.set(f"{predicted_label} ({predicted_meaning})")
        
        # 绑定组合框改变事件
        self.label_combo.bind('<<ComboboxSelected>>', self.update_current_state)
        
        # 确保当前窗口获得焦点
        self.focus_set()
        
        # 更新状态栏
        self.update_status()
        self.update_history_display()
    
    def prev_item(self):
        """导航到上一张并保存当前标注"""
        # 保存当前标注前先更新状态
        self.update_current_state()
        self.save_correction()
        
        if self.current_index > 0:
            self.current_index -= 1
            self.display_item(self.current_index)
    
    def next_item(self):
        """导航到下一张并保存当前标注"""
        # 保存当前标注前先更新状态
        self.update_current_state()
        self.save_correction()
        
        if self.current_index < self.total_items - 1:
            self.current_index += 1
            self.display_item(self.current_index)
    
    def save_correction(self):
        """保存当前标注并记录正确标注"""
        # 获取当前原始索引
        original_index = self.filtered_indices[self.current_index]
        item = self.dataset["annotations"][original_index]
        
        # 获取当前状态
        current_state = self.current_states[original_index]
        selected_label = current_state["corrected_label"]
        
        # 如果标签没有变化，但用户点击了下一张，也视为确认
        if item.get("corrected_label") == selected_label:
            # 如果标签没有变化，但之前没有标记为确认，也视为确认
            if selected_label != "unknown" and selected_label == item["predicted_label"]:
                self.confirmed_count += 1
                self.update_status()
            return
        
        # 保存当前标注
        self.dataset["annotations"][original_index]["corrected_label"] = selected_label
        
        # 添加到历史记录
        self.annotation_history[original_index].append(copy.deepcopy(current_state))
        
        # 如果是"unknown"标签，不记录到正确标注文件
        if selected_label != "unknown":
            # 记录正确标注
            self.record_correct_annotation(item, selected_label)
            
            # 增加已确认标注计数
            if selected_label == item["predicted_label"]:
                self.confirmed_count += 1
                self.update_status()
    
    def record_correct_annotation(self, item, corrected_label):
        """记录正确标注到文件"""
        # 确保切片路径是绝对路径
        slice_path = item.get("slice_path", "")
        if slice_path and not os.path.isabs(slice_path):
            slice_path = os.path.join(self.dataset_path, slice_path)
        
        # 构建正确标注记录
        correct_annotation = {
            "position": item["position"],
            "original_label": item["predicted_label"],
            "corrected_label": corrected_label,
            "confidence": item["confidence"],
            "slice_path": slice_path,
            "timestamp": time.strftime("%Y-%m-%d %H:%M:%S")
        }
        
        # 加载现有正确标注（如果存在）
        correct_annotations = []
        if os.path.exists(self.correct_annotations_file):
            with open(self.correct_annotations_file, 'r') as f:
                try:
                    correct_annotations = json.load(f)
                except json.JSONDecodeError:
                    correct_annotations = []
        
        # 添加新标注
        correct_annotations.append(correct_annotation)
        
        # 保存更新后的正确标注
        with open(self.correct_annotations_file, 'w') as f:
            json.dump(correct_annotations, f, indent=4)
    
    def save_dataset(self):
        """保存整个数据集"""
        metadata_path = os.path.join(self.dataset_path, "metadata.json")
        try:
            with open(metadata_path, "w") as f:
                json.dump(self.dataset, f, indent=2)
            return True
        except Exception as e:
            messagebox.showerror("保存失败", f"保存数据集时出错: {str(e)}")
            return False
    
    def save_and_exit(self):
        """保存并退出"""
        # 保存当前标注
        self.save_correction()
        
        if self.save_dataset():
            messagebox.showinfo("保存成功", "标注已保存")
            self.destroy()
        else:
            messagebox.showwarning("保存失败", "标注保存失败，请重试")
    
    def calculate_accuracy(self):
        """计算识别正确率"""
        total = 0
        correct = 0
        for idx in self.filtered_indices:
            item = self.dataset["annotations"][idx]
            if "corrected_label" in item and item["corrected_label"] is not None:
                total += 1
                if item["predicted_label"] == item["corrected_label"]:
                    correct += 1
        
        return correct / total * 100 if total > 0 else 0
    
    def on_closing(self):
        """窗口关闭时的处理"""
        # 保存当前标注
        self.save_correction()
        
        if self.confirmed_count > 0:
            if messagebox.askyesno("保存", "是否保存修改后的标注？"):
                self.save_and_exit()
            else:
                self.destroy()
        else:
            self.destroy()










